package com.xqboss.apps.service.user;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xqboss.apps.constant.RedisKey;
import com.xqboss.apps.domain.user.UserIdentity;
import com.xqboss.apps.domain.user.UserIdentityPermGuild;
import com.xqboss.apps.dto.guild.WebGuildEquityConfigDto;
import com.xqboss.apps.enums.nft.UserNftStatusEnum;
import com.xqboss.apps.enums.user.UserWalletEnum;
import com.xqboss.apps.enums.user.WalletChangeDirectionEnum;
import com.xqboss.apps.enums.user.WalletChangeTypeEnum;
import com.xqboss.apps.enums.user.WalletWrenchChangeTypeEnum;
import com.xqboss.apps.mapper.user.UserIdentityPermGuildMapper;
import com.xqboss.apps.service.nft.NftService;
import com.xqboss.apps.vo.guild.GuildCreateConsumeVo;
import com.xqboss.apps.vo.guild.WebGuildEquityConfigVo;
import com.xqboss.common.core.redis.RedisCache;
import com.xqboss.common.exception.ServiceException;
import com.xqboss.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * (GuildEquityConfig)表服务实现类
 *
 *
 * @author LJH
 * @since 2024-01-09 17:21:10
 */
@Service
public class UserIdentityPermGuildService extends ServiceImpl<UserIdentityPermGuildMapper, UserIdentityPermGuild>  {

    @Autowired
    private RedisCache redisCache;
    @Autowired
    private UserIdentityService userIdentityService;
    @Autowired
    private SysUserNftService sysUserNftService;//用户藏品
    @Autowired
    private NftService nftService;//藏品
    @Autowired
    private SysUserWalletService userWalletService;
    @Autowired
    private UserAssetWrenchService userAssetWrenchService;
    @Autowired
    private SysUserWalletService sysUserWalletService;//消耗热豆
    @Autowired
    private UserAssetWrenchRecordService userAssetWrenchRecordService;//消耗扳手

    /**
     * 公会权益配置-新增或修改
     *
     * @param dto 传输对象
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveOrUpdateEquityConfig(WebGuildEquityConfigDto dto) {
        if (dto.getId()==null) {
            //新增
            this.lambdaQuery().eq(UserIdentityPermGuild::getIdentityId,dto.getIdentityId()).oneOpt().ifPresent(a->{
                throw new ServiceException("该身份的公会权益配置已存在！");
            });
        }
        UserIdentityPermGuild config = BeanUtil.toBean(dto, UserIdentityPermGuild.class);
        boolean b = this.saveOrUpdate(config);
        if(config.getCreateNftId()==null || config.getJoinNftId()==null){
            //mybatis-plus的update方法，如果字段为null，则不会更新该字段，所以这里手动更新
            this.lambdaUpdate()
                    .set(UserIdentityPermGuild::getCreateNftId,config.getCreateNftId())
                    .set(UserIdentityPermGuild::getJoinNftId,config.getJoinNftId())
                    .eq(UserIdentityPermGuild::getIdentityId,config.getIdentityId())
                    .update();
        }
        Assert.isTrue(b,()->new ServiceException("操作公会权益配置失败！"));
        redisCache.deleteObject(RedisKey.KEY_USER_GUILD_EQUITY_CONF+ dto.getIdentityId());
        redisCache.deleteObject(RedisKey.KEY_USER_GUILD_EQUITY_CONFS);
    }

    /**
     * 根据身份id获取当前身份配置的公会权益配置
     *
     * @param identityId 身份id
     * @return 权益配置
     */
    public WebGuildEquityConfigVo getGuildEquityConfig(Long identityId) {
        String redisCacheKey = RedisKey.KEY_USER_GUILD_EQUITY_CONF+ identityId;
        WebGuildEquityConfigVo vo = redisCache.getCacheObject(redisCacheKey);
        if (ObjUtil.isNotNull(vo)) {
            return vo;
        }
        UserIdentityPermGuild config = this.lambdaQuery().eq(UserIdentityPermGuild::getIdentityId, identityId).one();
        if(ObjUtil.isEmpty(config)){
            return new WebGuildEquityConfigVo();
        }
        vo = BeanUtil.toBean(config,WebGuildEquityConfigVo.class);
        vo.setCreateNftName(vo.getCreateNftId()==null?null:nftService.getNft(vo.getCreateNftId()).getTitle());
        vo.setJoinNftName(vo.getJoinNftId()==null?null:nftService.getNft(vo.getJoinNftId()).getTitle());
        redisCache.setCacheObject(redisCacheKey,vo);
        return vo;
    }

    /**
     * 获取所有身份的公会权益配置
     *
     * @return 公会权益配置
     */
    public List<UserIdentityPermGuild> getAll(){
        List<UserIdentityPermGuild> conf = redisCache.getCacheObject(RedisKey.KEY_USER_GUILD_EQUITY_CONFS);
        if (ObjUtil.isNotNull(conf)) {
            return conf;
        }
        List<UserIdentityPermGuild> list = this.lambdaQuery().orderByAsc(UserIdentityPermGuild::getIdentityId).list();
        Assert.isFalse(list.isEmpty(),()->new ServiceException("后台还未进行公会权益配置！"));
        redisCache.setCacheObject(RedisKey.KEY_USER_GUILD_EQUITY_CONFS,list);
        return list;
    }

    /**
     * 创建公会-权益配置
     * 获取所有身份的公会权益配置,按身份id进行分组
     *
     * @return 公会权益配置
     */
    public Map<Long,List<UserIdentityPermGuild>> getAllGuildCreate(){
        List<UserIdentityPermGuild> all = getAll();
        // 验证是否所有身份的公会权益配置中的创建公会开关豆关闭了
        Assert.isFalse(all.stream().allMatch(a -> a.getIsOpen() == Boolean.FALSE),()->new ServiceException("公会创建维护中~"));
        // 按身份id分组
        return all.stream().collect(Collectors.groupingBy(UserIdentityPermGuild::getIdentityId));
    }

    /**
     * 加入公会-权益配置
     * 获取所有身份的公会权益配置,按身份id进行分组
     *
     * @return 公会权益配置
     */
    public Map<Long,List<UserIdentityPermGuild>> getAllGuildJoin(){
        List<UserIdentityPermGuild> all = getAll();
        // 按身份id分组
        Map<Long, List<UserIdentityPermGuild>> map = all.stream().collect(Collectors.groupingBy(UserIdentityPermGuild::getIdentityId));
        // 每个身份不同公会等级的 加入队伍的公会权益 按公会等级升序排序
        map.forEach((k,v)->v.sort(Comparator.comparing(UserIdentityPermGuild::getGuildLevel)));
        return map;
    }

    /**
     * 获取当前用户创建公会消耗材料
     *
     * @param userId 用户id
     * @return 创建公会消耗材料
     */
    public GuildCreateConsumeVo getGuildCreateConsume(Long userId) {
        //1. 获取用户身份id
        Long identityId = userIdentityService.getByUserId(userId).getId();
        //2. 获取用户公会权益配置
        Map<Long, List<UserIdentityPermGuild>> guildConfMap = getAllGuildCreate();
        //3. 获取已开启创建公会的最低用户身份(身份id数据库自增，可以比较大小)
        UserIdentityPermGuild permGuild = null;
        for (Map.Entry<Long, List<UserIdentityPermGuild>> entry : guildConfMap.entrySet()) {
            Long k = entry.getKey();
            List<UserIdentityPermGuild> v = entry.getValue();
            if (v.get(0).getIsOpen()) {
                String identityName = userIdentityService.lambdaQuery().select(UserIdentity::getName).eq(UserIdentity::getId, k).one().getName();
                Assert.isFalse(identityId<k,()->new ServiceException(String.format("%s以上身份才能创建公会~",identityName)));
                permGuild = v.get(0);
                // 若用户的身份大于最低用户身份，且配置了，则取自己身份配置的公会权益
                if (identityId > k) {
                    String identityName1 = userIdentityService.lambdaQuery().select(UserIdentity::getName).eq(UserIdentity::getId, identityId).one().getName();
                    Assert.isFalse(!guildConfMap.containsKey(identityId),()->new ServiceException(String.format("%s身份的创建公会权益还未配置！",identityName1)));
                    Assert.isFalse(!(permGuild = guildConfMap.get(identityId).get(0)).getIsOpen(),()->new ServiceException(String.format("%s身份的创建公会权益未开启~",identityName1)));
                }
                break;
            }
        }

        //4. 满足创建公会的身份等级，且创建公会已开启，返回需要消耗的材料信息
        return getGuildConsume(permGuild,userId,Boolean.TRUE);
    }

    /**
     * 获取当前用户加入公会消耗材料
     *
     * @param userId 用户id
     * @return 加入公会消耗材料
     */
    public GuildCreateConsumeVo getGuildJoinConsume(Long userId) {
        //1. 获取用户身份id
        Long identityId = userIdentityService.getByUserId(userId).getId();
        //2. 获取用户公会权益配置
        Map<Long, List<UserIdentityPermGuild>> guildConfMap = getAllGuildJoin();
        //3. 若后台配置了该用户身份加入公会要消耗的材料，则返回材料信息
        UserIdentityPermGuild permGuild = guildConfMap.containsKey(identityId)?guildConfMap.get(identityId).get(0):null;
        //4. 满足创建公会的身份等级，且创建公会已开启，返回需要消耗的材料信息
        return getGuildConsume(permGuild,userId,Boolean.FALSE);
    }

    /**
     * 封装需要消耗的材料(创建公会 或 加入公会)
     *
     * @param permGuild 公会权益配置
     * @param userId 用户id
     * @param isCreate true -创建公会,false-加入公会
     * @return 封装
     */
    public GuildCreateConsumeVo getGuildConsume(UserIdentityPermGuild permGuild, Long userId, boolean isCreate) {
        if (ObjUtil.isEmpty(permGuild)){
            return null;
        }
        // 没有配置消耗材料（藏品、热豆、扳手）直接返回空，不需要消耗材料即可创建公会
        boolean noNft = false,noHotBeans = false,noWrenchs = false;
        if(isCreate){
            //创建公会
            noNft = permGuild.getCreateNftId()==null;
            noHotBeans = permGuild.getCreateHotBeans().compareTo(BigDecimal.ZERO)==0;
            noWrenchs = permGuild.getCreateWrench().compareTo(BigDecimal.ZERO)==0;
        }else{
            //加入公会
            noNft = permGuild.getJoinNftId()==null;
            noHotBeans = permGuild.getJoinHotBeans().compareTo(BigDecimal.ZERO)==0;
            noWrenchs = permGuild.getJoinWrench().compareTo(BigDecimal.ZERO)==0;
        }
        if (noNft && noHotBeans && noWrenchs) {
            return null;
        }
        // 根据有无配置材料，来获取要消耗的
        Long nftNumberId = null; String nftImgPath = null;
        if (!noNft) {
            // 判断用户有无藏品，则返回对应的藏品id；如果有多个返回最先拥有的藏品
            nftNumberId = this.sysUserNftService.getAnyNftNumber(isCreate?permGuild.getCreateNftId():permGuild.getJoinNftId(), userId);
            // 根据 藏品id 获取 藏品主图地址
            nftImgPath = this.nftService.getNftMainImg(isCreate?permGuild.getCreateNftId():permGuild.getJoinNftId());
        }
        boolean isHaveHotBeans = false,isHaveWrenchs = false;
        BigDecimal consumeHotBeans = BigDecimal.ZERO,consumeWrenchs=BigDecimal.ZERO;
        if (!noHotBeans) {
            // 获取热豆余额
            BigDecimal hotBeansBalance = this.userWalletService.getWallet(userId, UserWalletEnum.INTEGRAL).getAmount();
            // 热豆是否充足
            isHaveHotBeans = hotBeansBalance.compareTo(consumeHotBeans=isCreate?permGuild.getCreateHotBeans():permGuild.getJoinHotBeans())>=0;
        }
        if (!noWrenchs) {
            // 获取扳手余额
            BigDecimal WrenchBalance = this.userAssetWrenchService.getWrench(userId).getAmount();
            // 扳手是否充足
            isHaveWrenchs = WrenchBalance.compareTo(consumeWrenchs=isCreate?permGuild.getCreateWrench():permGuild.getJoinWrench())>=0;

        }
        return GuildCreateConsumeVo.builder()
                .isNft(!noNft)
                .isHotBeans(!noHotBeans)
                .isWrenchs(!noWrenchs)
                .isHaveNft(nftNumberId!=null)
                .isHaveHotBeans(isHaveHotBeans)
                .isHaveWrenchs(isHaveWrenchs)
                .nftNumberId(nftNumberId)
                .nftImgPath(nftImgPath)
                .consumeHotBeans(consumeHotBeans)
                .consumeWrenchs(consumeWrenchs)
                .build();
    }

    /**
     * 创建公会 或 申请加入 公会 材料的消耗
     *
     * 1.配置了，且拥有藏品，则消耗藏品
     * 2.配置了，热豆充足，则消耗热豆
     * 3.配置了，扳手充足，则消耗扳手
     *
     * @param vo 材料
     * @param userId 用户id
     * @param isCreate true-是创建公会
     */
    @Transactional(rollbackFor = Exception.class)
    public void startConsume(GuildCreateConsumeVo vo,Long userId,boolean isCreate) {
        Assert.isFalse(ObjUtil.isEmpty(vo),()->new ServiceException("消耗的材料信息不存在~"));
        String remarks = isCreate?"创建公会":"加入公会";
        //1.配置了，且拥有藏品，则消耗藏品
        if (vo.getIsNft()) {
            Assert.isTrue(vo.getIsHaveNft(),()->new ServiceException("还未拥有藏品，去市场逛逛吧~"));
            this.sysUserNftService.commonDestruction(userId, isCreate?UserNftStatusEnum.CREATE_GUILD_BURN:UserNftStatusEnum.JOIN_GUILD_BURN,vo.getNftNumberId());
        }
        //2.配置了，热豆充足，则消耗热豆
        if (vo.getIsHotBeans()) {
            Assert.isTrue(vo.getIsHaveHotBeans(),()->new ServiceException("热豆余额不足~"));
            this.sysUserWalletService.reduce(userId, UserWalletEnum.INTEGRAL,vo.getConsumeHotBeans(), isCreate?WalletChangeTypeEnum.CREATE_GUILD:WalletChangeTypeEnum.JOIN_GUILD,userId,remarks+"消耗");
        }
        //3.配置了，扳手充足，则消耗扳手
        if(vo.getIsWrenchs()){
            Assert.isTrue(vo.getIsHaveWrenchs(),()->new ServiceException("扳手余额不足~"));
            this.userAssetWrenchRecordService.updateWrenchBalance(userId,isCreate?WalletWrenchChangeTypeEnum.CREATE_GUILD:WalletWrenchChangeTypeEnum.JOIN_GUILD, WalletChangeDirectionEnum.OUT,vo.getConsumeWrenchs(), userId, remarks+"消耗");
        }
    }
}

